
    varying vec2 vUv;
    varying vec3 v_Normal;
    varying vec3 v_Position;

    void main() {
        vUv = uv;

        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
        gl_Position = projectionMatrix * mvPosition;

        v_Normal = normalize(normalMatrix * normal);
        v_Position = vec3(mvPosition.xyz);
    }


    uniform sampler2D tDiffuse;
    uniform float h;

    varying vec2 vUv;

    void main() {
        vec4 sum = vec4(0.0);

        vec4 originalSample = texture2D(tDiffuse, vUv);

        sum += texture2D(tDiffuse, vec2(vUv.x - 3.2307 * h, vUv.y)) * 0.0702;
        sum += texture2D(tDiffuse, vec2(vUv.x - 1.3846 * h, vUv.y)) * 0.3162;
        sum += originalSample * 0.2270270270;
        sum += texture2D(tDiffuse, vec2(vUv.x + 1.3846 * h, vUv.y)) * 0.3162;
        sum += texture2D(tDiffuse, vec2(vUv.x + 3.2307 * h, vUv.y)) * 0.0702;

        gl_FragColor = sum;
    }

    uniform sampler2D tDiffuse;
    uniform float v;

    varying vec2 vUv;

    void main() {
        vec4 sum = vec4(0.0);

        vec4 originalSample = texture2D(tDiffuse, vUv);

        sum += texture2D(tDiffuse, vec2(vUv.x, vUv.y - 3.2307 * v)) * 0.0702;
        sum += texture2D(tDiffuse, vec2(vUv.x, vUv.y - 1.3846 * v)) * 0.3162;
        sum += originalSample * 0.2270270270;
        sum += texture2D(tDiffuse, vec2(vUv.x, vUv.y + 1.3846 * v)) * 0.3162;
        sum += texture2D(tDiffuse, vec2(vUv.x, vUv.y + 3.2307 * v)) * 0.0702;

        gl_FragColor = sum;
    }

    uniform sampler2D tDiffuse;
    uniform sampler2D tOverlay;

    varying vec2 vUv;

    void main() {
        vec4 regularScene = texture2D(tDiffuse, vUv);
        vec4 overlay = texture2D(tOverlay, vUv);

        float blurOpacity = 0.5;

        overlay.a *= blurOpacity;
        gl_FragColor = vec4(regularScene.rgb * (1.0 - overlay.a) + overlay.rgb * overlay.a, 1.0);
    }

    uniform sampler2D tEarth;
    uniform sampler2D tClouds;
    uniform float fTime;

    varying vec2 vUv;
    varying vec3 v_Normal;
    varying vec3 v_Position;

    void main() {
        vec4 earth = texture2D(tEarth, vec2(vUv.x + fTime * 0.5, vUv.y));
        vec4 clouds = texture2D(tClouds, vec2(vUv.x + fTime, vUv.y));

        vec4 diffuse = earth + clouds;

        vec3 v_LightPos = vec3( - 200.0, 200.0, -200.0);
        vec3 light_vec = normalize(v_LightPos - v_Position);

        float lightStrength = max(dot(v_Normal, light_vec), 0.05);

        gl_FragColor = diffuse * lightStrength;
    }

    $(document).ready(function() {
        Initialize();

        // Instantiate a texture loader.
        var loader = new THREE.TextureLoader();

        // Allow CORS for loading images from Amazon S3.
        loader.crossOrigin = '';

        // load a resource
        loader.load('images/earth_clouds_low_4096.jpg',
        // Function when resource is loaded
        function(cloudTexture) {
            loader.load('images/earth_color_low_4096.jpg',
            function(earthTexture) {
                PopulateScenes(earthTexture, cloudTexture);
                SetupComposers();

                animate();
            });
        });
    });

    ///Adjust the WebGL content when the browser window is resized.
    $(window).resize(function() {
        var width = window.innerWidth;
        var height = window.innerHeight;

        camera.aspect = width / height;
        camera.updateProjectionMatrix();
        renderer.setSize(width, height);

        sceneComposer.setSize(width, height);
        blurComposer.setSize(width / 2, height / 2);
    });

    // Scene-related variables that will be needed throughout various functions.
    var camera = null;
    var renderer = null;
    var scene = null;
    var controls = null;
    var blurScene = null;
    var blurMaskScene = null;

    var blurComposer = null;
    var sceneComposer = null;

    var earthMaterial = null;
    var earthGlow = null;

    var startTime = new Date().getTime();

    // Initialize the scene camera, renderer and composers.
    var Initialize = function() {
        camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 2000);
        camera.position.z = 400;
        camera.position.y = -80;
        camera.position.x = 100;

        renderer = new THREE.WebGLRenderer({
            alpha: true,
            logarithmicDepthBuffer: true,
            antialias: true

        });
        renderer.setClearColor(0xFFFFFF, 0.0);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.autoClear = false;
        renderer.setClearAlpha(0);
        $('#glContainer').append(renderer.domElement);

        /*
    controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.enableDamping = true;
    controls.dampingFactor = 0.25;
    controls.enableZoom = true;
    */

        scene = new THREE.Scene();
        blurScene = new THREE.Scene();
        blurMaskScene = new THREE.Scene();

        var renderTargetParameters = {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
            format: THREE.RGBAFormat,
            stencilBufer: true
        };
        var blurRenderTarget = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, renderTargetParameters);

        blurComposer = new THREE.EffectComposer(renderer, blurRenderTarget);
        sceneComposer = new THREE.EffectComposer(renderer);
    }

    // Set up the main scene, blur scene, and blur mask.
    var PopulateScenes = function(earthTexture, cloudTexture) {
        earthMaterial = CreateEarthMaterial(earthTexture, cloudTexture);

        var earthPos = new THREE.Vector3(40, -100, 0);
        var sunPos = new THREE.Vector3(20, -5, 0);

        // This cube will be the source from which the glow emanates.
        var earth = CreateEarth({
            size: 50,
            color: 0xFFFFFF,
            material: earthMaterial,
            position: earthPos
        });

        var sun = CreateSun({
            size: 10,
            color: 0xFFFFFF,
            opacity: 0.5,
            position: sunPos
        });

        // This cube effectively creates a mask in the blur scene. It mirrors the position of the opaque cube.
        // If you examine the 'CreateCube' method, you'll see that we are creating a material with an opacity of 0, but a disabled transparent flag.
        // This material is equivalent to the following fragment shader: "gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);"
        var earthObstruction = CreateEarth({
            size: 49,
            color: 0xFFFFFF,
            opacity: 0.0,
            position: earthPos
        });

        earthObstruction.renderOrder = 1;

        // This is the cube that represents the actual glow of the first cube we created.
        // Notice its size is slightly bigger than the source cube. The size can be adjusted creating a smaller/larger glow.
        earthGlow = CreateEarth({
            size: 51.5,
            opacity: 0.7,
            color: 0x397ebe,
            position: earthPos
        });

        var sunGlow = CreateSun({
            size: 12,
            color: 0xFFFFFF,
            position: sunPos
        });

        // This cube represents the area of the scene that needs to be blurred.
        // This object will be used
        var glowCubeMask = CreateEarth({
            size: 65,
            color: 0xFFFFFF,
            position: earthPos
        });

        // Set up a point light in the main scene.
        var light = new THREE.PointLight(0xff0000, 1, 1000);
        light.position.set(50, 50, 50);

        // Mirror the same light in the blur scene.
        var blurLight = new THREE.PointLight(0xff0000, 1, 1000);
        blurLight.position.set(50, 50, 50);

        scene.add(earth);
        //scene.add(sun);
        scene.add(new THREE.AmbientLight(0xFFFFFF));

        blurScene.add(new THREE.AmbientLight(0xFFFFFF));
        blurScene.add(earthGlow);
        blurScene.add(earthObstruction);
        //blurScene.add(sunGlow);
        blurMaskScene.add(glowCubeMask);
    }

    var CreateEarthMaterial = function(earthMap, cloudMap) {
        cloudMap.wrapS = cloudMap.wrapT = THREE.RepeatWrapping;
        earthMap.wrapS = earthMap.wrapT = THREE.RepeatWrapping;

        return new THREE.ShaderMaterial({
            uniforms: {
                tEarth: {
                    value: earthMap
                },
                tClouds: {
                    value: cloudMap
                },
                fTime: {
                    value: 0.0
                }
            },
            vertexShader: $('#simpleVertex').html(),
            fragmentShader: $('#earthFrag').html()
        });
    }

    // Helper function that will create a cube and cube material based on the function parameters.
    // Possible parameters: color, size, opacity, position
    var CreateEarth = function(params) {
        var earthGeometry = new THREE.SphereGeometry(params.size, 50, 50);

        var material = params.material != undefined ? params.material: new THREE.MeshLambertMaterial({
            color: params.color,
            opacity: params.opacity != undefined ? params.opacity: 1.0,
            transparent: false,
            depthWrite: false,
            shading: THREE.SmoothShading
        });

        var earth = new THREE.Mesh(earthGeometry, material);

        if (params.position != undefined) earth.position.copy(params.position);

        return earth;
    }

    var CreateSun = function(params) {
        var earthGeometry = new THREE.IcosahedronGeometry(params.size, 4);

        var material = params.material != undefined ? params.material: new THREE.MeshLambertMaterial({
            color: params.color,
            opacity: params.opacity != undefined ? params.opacity: 1.0,
            transparent: false,
            depthWrite: false,
            shading: THREE.SmoothShading
        });

        var earth = new THREE.Mesh(earthGeometry, material);

        if (params.position != undefined) earth.position.copy(params.position);

        return earth;
    }

    // Set up THREE.Composer objects to render / blur the scenes.
    var SetupComposers = function() {
        // Create a couple blur passes to blur the regular scene output. Note that each one of these objects will have a horizontal blur pass and a vertical blur pass.
        var blur1Passes = CreateBlurShaderPasses(window.innerWidth, window.innerHeight, 8);
        var blur2Passes = CreateBlurShaderPasses(window.innerWidth, window.innerHeight, 2);

        var blurPass = new THREE.RenderPass(blurScene, camera);
        blurPass.clear = true;
        blurPass.clearAlpha = 0.0;

        var maskPass = new THREE.MaskPass(blurMaskScene, camera);
        var clearMaskPass = new THREE.ClearMaskPass();

        blurComposer.addPass(blurPass);
        blurComposer.addPass(maskPass);
        blurComposer.addPass(blur1Passes.horizontalPass);
        blurComposer.addPass(blur1Passes.verticalPass);
        blurComposer.addPass(blur2Passes.horizontalPass);
        blurComposer.addPass(blur2Passes.verticalPass);
        blurComposer.addPass(clearMaskPass);

        // Set up a simple shader that will overlay the blurred scene image over the regular scene image.
        var overlayShader = {
            uniforms: {
                tDiffuse: {
                    type: "t",
                    value: 0,
                    texture: null
                },
                // The base scene buffer
                tOverlay: {
                    type: "t",
                    value: 1,
                    texture: null
                } // The glow scene buffer
            },

            vertexShader: $('#simpleVertex').html(),
            fragmentShader: $('#overlayFrag').html()
        };

        var scenePass = new THREE.RenderPass(scene, camera);
        scenePass.clear = true;

        overlayShader.uniforms["tOverlay"].value = blurComposer.renderTarget2;
        var overlayPass = new THREE.ShaderPass(overlayShader);
        overlayPass.renderToScreen = true;

        sceneComposer.addPass(scenePass);
        sceneComposer.addPass(overlayPass);
    }

    // This function simply creates a pair of THREE.ShaderPass objects (horizontal and vertical passes).
    // Since we are using the THREEJS composer object, the blur shader uniforms must be specifically named to fit into the THREEJS composer pipeline.
    // In a THREEJS composer, each pass takes in the result of the previous pass via a texture called 'tDiffuse'.
    var CreateBlurShaderPasses = function(h, v, blurriness) {
        var HBlurShader = {
            uniforms: {
                tDiffuse: {
                    type: "t",
                    value: null
                },
                h: {
                    type: "f",
                    value: blurriness / h
                }
            },
            vertexShader: $('#simpleVertex').html(),
            fragmentShader: $('#horizontalBlurFrag').html()
        };

        var VBlurShader = {
            uniforms: {
                tDiffuse: {
                    type: "t",
                    value: null
                },
                v: {
                    type: "f",
                    value: blurriness / v
                }
            },
            vertexShader: $('#simpleVertex').html(),
            fragmentShader: $('#verticalBlurFrag').html()
        };

        var HBlur = new THREE.ShaderPass(HBlurShader);
        var VBlur = new THREE.ShaderPass(VBlurShader);

        return {
            horizontalPass: HBlur,
            verticalPass: VBlur
        };
    }

    var RenderGlowScene = function() {
        //controls.update();
        var ticks = new Date().getTime() - startTime;

        // Update the 'time' value of the background shader to a scaled value based on the number of seconds that have elapsed.
        earthMaterial.uniforms.fTime.value = ticks / 70000.0;

        blurComposer.render();
        sceneComposer.render();
    }

    function animate() {
        requestAnimationFrame(animate);

        RenderGlowScene();
    }

    function fans() {
        //renderer.clear();
        if (camera.position.x != 40) {
            camera.position.x = camera.position.x - 1;
            renderer.render(scene, camera);
        }
        if (camera.position.x == 40) {
            camera.position.z = camera.position.z - 1;
            renderer.render(scene, camera);
        }
        requestAnimationFrame(fans);
    }
